package com.jxtc.enterprise.common.service;

import com.alibaba.fastjson.JSONObject;
import com.jxtc.enterprise.common.vo.DeliveryLockerOpenDoorParameterVO;
import com.jxtc.enterprise.common.vo.DeliveryLockerOpenDoorVO;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.text.MessageFormat;
import java.util.*;

@Service
@Slf4j
public class DeliveryLockerOpenApiService {
    public final String OPEN_DOOR_URL = "http://easivend.net.cn/evrunst/qrcode/api/openddoor";
    public final String OPEN_DOOR_RESULT_URL = "http://easivend.net.cn/evrunst/qrcode/api/openddoorResult";

    private final RestTemplate restTemplate;

    public DeliveryLockerOpenApiService(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }


    /**
     * 打开多个柜门（同时最多打开 5 个柜门）
     *
     * @param clientNo  售货机编号
     * @param orderNo   订单号 某次调用接口，打开一批5个窗格的 任务id
     * @param appId     用户id
     * @param appKey    用户密码
     * @param parameter 相关机器货道信息，list 后的数组有几个就对应开几个门，最少需要开一个门，请注意，最多只能开 5个货道
     */
    public void openDoorList(String clientNo, String orderNo, String appId, String appKey, List<DeliveryLockerOpenDoorParameterVO> parameter, int waitTimeForQueryOpenDoorResultInMs) {
        String requestId = UUID.randomUUID().toString();
        if (parameter.isEmpty() || parameter.size() > 5) {
            log.error("parameter size must be between 1 and 5");
            throw new IllegalArgumentException("parameter size must be between 1 and 5");
        }

        // 构建符合接口需求的 parameter 参数
        String parameterJsonStr = buildParameter(parameter);
        openDoorWithOk3Client(requestId, appId, appKey, clientNo, orderNo, parameterJsonStr);
        // 调用开柜门接口后，等待若干秒，可以通过页面上输入，当某次点击开全部柜门，某个柜门没有卡启，可能是 批次 接口调用之间 间隔太短，可以通过页面输入来延长时间
        try {
            Thread.sleep(waitTimeForQueryOpenDoorResultInMs);
        } catch (InterruptedException e) {
            log.error("sleep before query_open_door_result failed, ex: ", e);
        }
    }


    private void openDoorWithOk3Client(String requestId, String appId, String appKey, String clientNo, String orderNo, String parameterJsonStr) {

        log.info("{}, ==================== starting call open door api ====================", requestId);
        String respBody = "";
        Response response = null;
        try {
            OkHttpClient client = new OkHttpClient().newBuilder()
                    .build();
            MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
            String content = MessageFormat.format("app_id={0}&app_key={1}&client_no={2}&order_no={3}&parameter={4}", appId, appKey, clientNo, orderNo, parameterJsonStr);
            log.info("{}, openDoorList, request body: {}", requestId, content);
            RequestBody body = RequestBody.create(content, mediaType);
            Request request = new Request.Builder()
                    .url("http://easivend.net.cn/evrunst/qrcode/api/openddoor")
                    .method("POST", body)
                    .addHeader("Content-Type", "application/x-www-form-urlencoded")
                    .build();
            response = client.newCall(request).execute();
            if (response.body() != null) {
                respBody = response.body().string();
                response.close();
            }
        } catch (IOException e) {
            respBody = e.getMessage();
            log.error("invoke delivery_locker_api failed, ex: ", e);
        } finally {
            if (response != null) {
                response.close();
            }
        }

        log.info("{}, openDoorList, response: {}", requestId, respBody);
        log.info("{}, =====================================================================\n", requestId);
    }


    /**
     * 打开一个柜门
     *
     * @param clientNo  售货机编号
     * @param orderNo   订单号
     * @param appId     用户id
     * @param appKey    用户密码
     * @param parameter 需要打开的那个货道的信息
     */
    public void openDoor(String clientNo, String orderNo, String appId, String appKey, DeliveryLockerOpenDoorParameterVO parameter, int sleepMs) {
        // 调用openDoorList方法，传入需要打开的货道信息
        openDoorList(clientNo, orderNo, appId, appKey, Collections.singletonList(parameter), sleepMs);
    }

    public void openDoorWithoutSleep(String clientNo, String orderNo, String appId, String appKey, DeliveryLockerOpenDoorParameterVO parameter) {
        String requestId = UUID.randomUUID().toString();
        // 构建符合接口需求的 parameter 参数
        String parameterJsonStr = buildParameter(Collections.singletonList(parameter));
        openDoorWithOk3Client(requestId, appId, appKey, clientNo, orderNo, parameterJsonStr);
    }


    /**
     * 当需要打开的柜门数量多余 5 个时，按照 5 个一分组依次发起请求
     */
    public void openDoorListMoreThanFive(String clientNo, String appId, String appKey, List<DeliveryLockerOpenDoorParameterVO> parameter, int waitTimeForQueryOpenDoorResultInMs) {
        int groupSize = 5;
        int total = parameter.size();
        for (int i = 0; i < total; i += groupSize) {
            int end = Math.min(total, i + groupSize);
            // 每 5 个窗格，调用一次接口，打开5个窗口，接口中的 orderNo 等同于某次接口调用的 批次id，parameter 中的list中的 sn 为每一个窗口的唯一标识
            ArrayList<DeliveryLockerOpenDoorParameterVO> parameterInGroup = new ArrayList<>(parameter.subList(i, end));
            // 该批次5个开窗格参数，orderNo相同，orderNo 等同于一次接口调用的 任务Id 或 批次id
            String orderNo = parameterInGroup.get(0).getOrderNo();
            // 每 5 个一组发起请求
            openDoorList(clientNo, orderNo, appId, appKey, parameterInGroup, waitTimeForQueryOpenDoorResultInMs);
        }
    }

    /**
     * 查询出货记录。如果后台还没有返回出货结果，就不会返回 status 字段。过段时间如果还是没有收到这个字段，则说明出货失败。status: 1 出货成功, 2 出货失败
     *
     * @param clientNo 售货机编号
     * @param orderNo  订单号
     * @param appId    用户id
     * @param appKey   用户密码
     */
    public void queryOpenDoorResult(String appId, String appKey, String clientNo, String orderNo) {

        OkHttpClient client = new OkHttpClient().newBuilder().build();
        MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
        String content = MessageFormat.format("app_id={0}&app_key={1}&client_no={2}&order_no={3}", appId, appKey, clientNo, orderNo);
        log.info("requestId: {}, query_open_door_result, requestBody: {}", orderNo, content);
        RequestBody body = RequestBody.create(content, mediaType);
        Request request = new Request.Builder()
                .url("http://easivend.net.cn/evrunst/qrcode/api/openddoorResult")
                .method("POST", body)
                .addHeader("Content-Type", "application/x-www-form-urlencoded")
                .build();
        try (Response response = client.newCall(request).execute()) {
            if (response.body() != null) {
                String responseBody = response.body().string();

                log.info("requestId:{} queryOpenDoorResult, response: {}", orderNo, responseBody);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 构建三方接口中 parameter 字段所需的 JSON 字符串
     */
    private String buildParameter(List<DeliveryLockerOpenDoorParameterVO> parameters) {
        List<Map<String, String>> list = new ArrayList<>(parameters.size());
        for (DeliveryLockerOpenDoorParameterVO parameter : parameters) {
            Map<String, String> map = new HashMap<>(8);
            map.put("order_no", parameter.getOrderNo());
            map.put("cab", parameter.getCab());
            map.put("sn", parameter.getSn());
            map.put("col", parameter.getCol());
            map.put("price", parameter.getPrice());
            map.put("no", parameter.getNo());
            map.put("name", parameter.getName());
            map.put("type", parameter.getType());

            list.add(map);
        }

        Map<String, List<Map<String, String>>> map = Collections.singletonMap("list", list);
        return JSONObject.toJSONString(map);
    }
}
